//=============================================================================
// CoolStorage.NET - .NET Object Relational Mapping Library 
//
// Copyright (c) 2004-2008 Philippe Leybaert
//
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//=============================================================================

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;

namespace Activa.CoolStorage
{
	public static class CSConfig
	{
	    internal const string DEFAULT_CONTEXTNAME = "_DEFAULT_";

		private static bool? _useTransactionScope = null;
		
		public static bool UseTransactionScope
		{
			get
			{
				if (_useTransactionScope == null)
				{
					NameValueCollection configurationSection = (NameValueCollection)System.Configuration.ConfigurationManager.GetSection("CoolStorage");

					if (configurationSection != null && configurationSection["UseTransactionScope"] != null)
						_useTransactionScope = (configurationSection["UseTransactionScope"].ToUpper() == "TRUE");
                    else
					    _useTransactionScope = false;
				}

				return _useTransactionScope ?? false;
			}
			set
			{
				_useTransactionScope = value;
			}
		}
		
        internal static Dictionary<string, string> ColumnMappingOverrideMap = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);

        private static readonly Dictionary<string, CSDataProvider> _globalDbMap = new Dictionary<string, CSDataProvider>(StringComparer.CurrentCultureIgnoreCase);
        private static readonly Dictionary<string, bool> _globalDbMapChanged = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);

		[ThreadStatic]
		private static ThreadData _threadData;


		/// <summary>
		/// Determines whether a database connection has been specified.
		/// </summary>
		/// <returns>
		/// 	<c>true</c> if this instance has DB; otherwise, <c>false</c>.
		/// </returns>
		public static bool HasDB()
		{
			return HasDB(DEFAULT_CONTEXTNAME);
		}

		/// <summary>
		/// Determines whether the database connection for the given context has been specified.
		/// </summary>
		/// <param name="contextName">Name of the context.</param>
		/// <returns>
		/// 	<c>true</c> if the specified context name has DB; otherwise, <c>false</c>.
		/// </returns>
		public static bool HasDB(string contextName)
		{
			lock(_globalDbMap)
			{
				return _globalDbMap.ContainsKey(contextName);
			}
		}

		internal static CSDataProvider GetDB(string strContext)
		{
			if (_threadData == null)
				_threadData = new ThreadData();

			return _threadData.GetDB(strContext);
		}

		public static void SetDB(CSDataProvider db)
		{
			SetDB(db,DEFAULT_CONTEXTNAME);
		}

		public static void SetDB(CSDataProvider db , string contextName)
		{
			lock(_globalDbMap)
			{
				_globalDbMap[contextName] = db;
			    _globalDbMapChanged[contextName] = true;
			}
		}

		public static void ChangeTableMapping(Type type , string tableName , string contextName)
		{
			CSSchema.ChangeMapTo(type,tableName,contextName);
		}

		public static void ChangeColumnMapping(Type type , string propertyName , string columnName)
		{
			lock (ColumnMappingOverrideMap)
			{
				PropertyInfo propInfo = type.GetProperty(propertyName);

				if (propInfo == null)
					throw new CSException("ChangeColumnMapping() : Property [" + propertyName + "] undefined");

				ColumnMappingOverrideMap[ propInfo.DeclaringType.Name + ":" + propInfo.Name ] = columnName;
			}
		}


        private class ThreadData
        {
            private readonly Thread _callingThread;
            private readonly Dictionary<string, CSDataProvider> _threadDbMap = new Dictionary<string, CSDataProvider>(StringComparer.CurrentCultureIgnoreCase);
            
            internal ThreadData()
            {
                _callingThread = Thread.CurrentThread;

                Thread cleanupThread = new Thread(CleanupBehind);

                cleanupThread.IsBackground = true;

                cleanupThread.Start();
            }

            internal CSDataProvider GetDB(string contextName)
            {
                lock (_globalDbMap)
                {
                    if (_globalDbMapChanged.ContainsKey(contextName) && _globalDbMapChanged[contextName])
                    {
                        _globalDbMapChanged[contextName] = false;

                        if (_threadDbMap.ContainsKey(contextName))
                        {
                            _threadDbMap[contextName].Dispose();
                            _threadDbMap.Remove(contextName);
                        }
                    }
                }

                if (_threadDbMap.ContainsKey(contextName))
                    return _threadDbMap[contextName];

                lock (_globalDbMap)
                {
                    if (!_globalDbMap.ContainsKey(contextName))
                    {
                        NameValueCollection configurationSection = (NameValueCollection)System.Configuration.ConfigurationManager.GetSection("CoolStorage");

                        if (configurationSection != null)
                        {
                            string key = (contextName == DEFAULT_CONTEXTNAME) ? "Connection" : ("Connection." + contextName);

                            string value = configurationSection[key];

                            if (value.IndexOf('/') > 0)
                            {
                                string dbType = value.Substring(0, value.IndexOf('/')).Trim();
                                string connString = value.Substring(value.IndexOf('/') + 1).Trim();

                                Type type = Type.GetType("Activa.CoolStorage." + dbType);

                                if (type != null)
                                {
                                    CSDataProvider dbObject = (CSDataProvider)Activator.CreateInstance(type, new object[] { connString });

                                    if (dbObject != null)
                                        _globalDbMap[contextName] = dbObject;
                                }
                            }
                        }

                        if (!_globalDbMap.ContainsKey(contextName))
                            throw new CSException("GetDB(): context [" + contextName + "] not found");
                    }
                }

                CSDataProvider db = _globalDbMap[contextName];

                db = db.Clone();

                _threadDbMap[contextName] = db;

                return db;
            }

            private void CleanupBehind()
            {
                _callingThread.Join();

                foreach (CSDataProvider db in _threadDbMap.Values)
                    db.Dispose();
            }
        }

	}
}
